package com.google.jessewilson.codesearch.ui.preflight;

import ca.odell.glazedlists.GlazedLists;
import com.google.inject.Inject;
import com.google.jessewilson.codesearch.api.Query;
import com.google.jessewilson.codesearch.api.SearchProviderFactory;
import com.google.jessewilson.codesearch.api.spi.SearchProviderBackend;
import com.google.jessewilson.codesearch.api.spi.SearchProviderParameters;
import com.google.jessewilson.codesearch.api.spi.SearchProviderParametersEditor;
import com.google.jessewilson.codesearch.configuration.Configuration;
import com.google.jessewilson.codesearch.ide.QueryHistory;
import com.google.jessewilson.codesearch.ui.EditorComponent;
import com.google.jessewilson.codesearch.ui.HistoryField;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

import javax.swing.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * Swing panel that allows the user to edit a query before running it.
 * Usually sits inside a dialog box, such as
 * {@link com.google.jessewilson.codesearch.intellij.PreflightQueryEditorDialog}
 *
 * @author jessewilson
 */
public class PreflightQueryEditor implements EditorComponent<Query> {

  private final HistoryField<Query> queryField;
  private final SearchProviderFactory searchProviderFactory;

  /**
   * Radio buttons for selecting which search provider to activate.
   */
  private final ButtonGroup activeButtonsGroup = new ButtonGroup();
  private final Map<SearchProviderParametersEditor, JRadioButton> editorToActiveButtonMap =
      new HashMap<SearchProviderParametersEditor, JRadioButton>();

  private final JPanel panel;

  private File editorFile;

  @Inject
  public PreflightQueryEditor(QueryHistory queryHistory,
      Configuration configuration,
      SearchProviderFactory searchProviderFactory) {

    this.searchProviderFactory = searchProviderFactory;

    this.queryField = new HistoryField<Query>(
        GlazedLists.<Query, String>beanFunction(Query.class, "searchTerm"));
    this.queryField.getComponent().setPrototypeDisplayValue(
        "EnumConstantNotPresentException");
    this.queryField.setHistory(queryHistory.getSnapshot());

    CellConstraints cc = new CellConstraints();

    FormLayout queryExtrasLayout = new FormLayout("30px, fill:pref:grow", "");
    JPanel queryExtrasPanel = new JPanel(queryExtrasLayout);

    int row = 1;
    for (Configuration.Entry entry : configuration.entries()) {
      if (entry.isEnabled()) {
        SearchProviderParametersEditor parametersEditor = 
            searchProviderFactory.getEditor(entry.getSearchProviderConfiguration());
        JRadioButton activeRadioButton =
            new JRadioButton(parametersEditor.getSearchProvider().getName());
        activeButtonsGroup.add(activeRadioButton);
        editorToActiveButtonMap.put(parametersEditor, activeRadioButton);

        // TODO: select the first search provider by default - this should be
        // set to whatever search provider was last used in the future.
        if (editorToActiveButtonMap.size() == 1) {
          activeRadioButton.setSelected(true);
        }

        // create the rows
        queryExtrasLayout.appendRow(new RowSpec("min"));
        queryExtrasLayout.appendRow(new RowSpec("min"));
        queryExtrasLayout.appendRow(new RowSpec("3px"));
        queryExtrasLayout.appendRow(new RowSpec("min"));
        queryExtrasLayout.appendRow(new RowSpec("20px"));

        // add the components
        queryExtrasPanel.add(new JSeparator(JSeparator.HORIZONTAL), cc.xywh(1, row++, 2, 1));
        queryExtrasPanel.add(activeRadioButton, cc.xywh(1, row++, 2, 1));
        row++;
        queryExtrasPanel.add(parametersEditor.getComponent(), cc.xy(2, row++));
        row++;
      }
    }

    FormLayout overallLayout = new FormLayout(
        "right:min, 3px, left:pref:grow",
        "min, 2px, min, 10px, min");
    panel = new JPanel(overallLayout);

    panel.add(new JLabel("Query:"), cc.xy(1, 1));
    panel.add(queryField.getComponent(), cc.xy(3, 1, "fill, default"));
    panel.add(queryExtrasPanel, cc.xywh(1, 5, 3, 1));
  }

  public SearchProviderBackend getActiveProvider() {
    for (SearchProviderParametersEditor editor : editorToActiveButtonMap.keySet()) {
      if (editorToActiveButtonMap.get(editor).isSelected()) {
        return searchProviderFactory.getSearchProvider(editor);
      }
    }
    throw new IllegalStateException("No search provider selected.");
  }

  /**
   * Sets the query to be edited.  It's assumed that the query has no query
   * extra.
   */
  public void setValue(Query query) {
    queryField.setValue(query.getSearchTerm());
    editorFile = query.getEditorFile();
  }

  /**
   * Returns the current value of this query editor.
   */
  public Query getValue() {
    String searchTerm = queryField.getValue();

    SearchProviderParameters searchProviderParameters = null;
    for (SearchProviderParametersEditor editor : editorToActiveButtonMap.keySet()) {
      if (editorToActiveButtonMap.get(editor).isSelected()) {
        searchProviderParameters = editor.getValue();
      }
    }

    return new Query.Builder()
        .searchTerm(searchTerm)
        .editorFile(editorFile)
        .searchProviderParameters(searchProviderParameters)
        .build();
  }

  public JComponent getComponent() {
    return panel;
  }
  
  /**
   * Set the action to execute when this form is submitted.
   */
  public void setAction(final Action action) {
    assert(action != null);
    queryField.setAction(action);
    for (SearchProviderParametersEditor searchProviderParametersEditor : editorToActiveButtonMap.keySet()) {
      searchProviderParametersEditor.setAction(action);
    }
  }

  // TODO(tmao): save state query editor, such that after performing one search,
  // the user will see the same settings when starting another one.
  // tmao(2007-09-11): Commenting this out pending discussion on how we're
  // going to handle externalizing state.

//  public static class MyExternalizer implements Externalizer<PreflightQueryEditor> {
//
//    private static final String CLASS = "class";
//    private static final String ATTRIBUTES = "attributes";
//
//    public PreflightQueryEditor readExternal(Element element) {
//      PreflightQueryEditor preflightQueryEditor = new PreflightQueryEditor();
//
//      EasyElement preflightQueryEditorElement = new EasyElement(element);
//      for (EasyElement queryExtraElement : preflightQueryEditorElement.getChildren()) {
//        String className = preflightQueryEditorElement.getString(CLASS, null);
//        Element attributesElement = queryExtraElement.getElement().getChild(ATTRIBUTES);
//        Externalizer<QueryExtra> externalizer = Finder.find(className);
//        QueryExtra queryExtra = externalizer.readExternal(attributesElement);
//
//      }
//    }
//
//    public void writeExternal(Element element, PreflightQueryEditor preflightQueryEditor) {
//      EasyElement preflightQueryEditorElement = new EasyElement(element);
//
//      for (QueryExtraEditor queryExtraEditor :
//          preflightQueryEditor.editorToActiveButtonMap.keySet()) {
//        QueryExtra queryExtra = queryExtraEditor.getValue();
//        String queryExtraClassName = queryExtra.getClass().getName();
//
//        EasyElement queryExtraElement = new EasyElement("queryExtra");
//        queryExtraElement.putString(CLASS, queryExtraClassName);
//        EasyElement attributesElement = new EasyElement(ATTRIBUTES);
//
//        Externalizer<QueryExtra> externalizer = Finder.find(queryExtraClassName);
//        externalizer.writeExternal(attributesElement.getElement(), queryExtra);
//
//        queryExtraElement.addChild(attributesElement);
//        preflightQueryEditorElement.addChild(queryExtraElement);
//      }
//    }
//  }
}
